package com.xumx.demo.customize.annotate.log;

import com.alibaba.fastjson.JSON;
import com.xumx.demo.pojo.MethodLogEntity;
import com.xumx.demo.service.MethodLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 方法日志切面，实现日志记录与数据库存储
 */
@Aspect
@Component
public class MethodLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(MethodLogAspect.class);

    private final MethodLogService logService;

    @Autowired
    public MethodLogAspect(MethodLogService logService) {
        this.logService = logService;
    }

    /**
     * 环绕通知，记录方法调用日志
     */
    @Around("@annotation(com.xumx.demo.customize.annotate.log.MethodLog)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名和注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        MethodLog methodLog = method.getAnnotation(MethodLog.class);

        // 创建日志实体
        MethodLogEntity logEntity = new MethodLogEntity();
        Date startTime = new Date();
        logEntity.setStartTime(startTime);
        logEntity.setClassName(joinPoint.getTarget().getClass().getName());
        logEntity.setMethodName(method.getName());
        logEntity.setMethodDescription(methodLog.description());

        // 获取请求信息（Web环境）
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                jakarta.servlet.http.HttpServletRequest request = attributes.getRequest();
                String userid = request.getHeader("userid");
                logEntity.setUserId(userid);
                logEntity.setRequestIp(request.getHeader("host"));
//                logEntity.setRequestIp(getClientIp((HttpServletRequest) request));

                // 从请求或会话中获取用户ID（实际项目中根据认证方式获取）
                Object userId = request.getAttribute("userId");
                if (userId == null) {
                    userId = request.getSession().getAttribute("userId");
                }
                if (userId != null) {
                    logEntity.setUserId(userId.toString());
                }
            }
        } catch (Exception e) {
            logger.warn("获取请求信息失败", e);
        }

        // 处理入参
        if (methodLog.logInput()) {
            String[] paramNames = signature.getParameterNames();
            Object[] paramValues = joinPoint.getArgs();
            Map<String, Object> params = new LinkedHashMap<>();

            // 处理参数，忽略指定索引的参数
            Set<Integer> ignoreIndexes = new HashSet<>();
            for (int index : methodLog.ignoreParamIndexes()) {
                ignoreIndexes.add(index);
            }

            for (int i = 0; i < paramNames.length; i++) {
                if (!ignoreIndexes.contains(i)) {
                    params.put(paramNames[i], paramValues[i]);
                } else {
                    params.put(paramNames[i], "***已忽略***");
                }
            }

            logEntity.setInputParams(JSON.toJSONString(params));
        }

        Object result = null;
        Exception exception = null;

        try {
            // 执行目标方法
            result = joinPoint.proceed();
            logEntity.setStatus(1); // 成功状态
            return result;
        } catch (Exception e) {
            exception = e;
            logEntity.setStatus(0); // 失败状态
            logEntity.setErrorMessage(e.getMessage());

            // 记录异常堆栈
            if (methodLog.logExceptionStack()) {
                logEntity.setErrorStack(getStackTraceAsString(e));
            }
            throw e; // 继续抛出异常，不影响原有业务逻辑
        } finally {
            // 处理出参
            if (methodLog.logOutput() && result != null) {
                logEntity.setOutputResult(JSON.toJSONString(result));
            }

            // 记录结束时间和耗时
            Date endTime = new Date();
            logEntity.setEndTime(endTime);
            logEntity.setCostTime(endTime.getTime() - startTime.getTime());

            // 保存到数据库（如果配置了需要保存）
            if (methodLog.saveToDb()) {
                try {
                    logService.saveLog(logEntity);
                } catch (Exception e) {
                    logger.error("保存日志到数据库失败", e);
                }
            }

            // 同时输出到控制台日志
            logger.info(buildLogString(logEntity, exception));
        }
    }

    /**
     * 构建日志字符串
     */
    private String buildLogString(MethodLogEntity logEntity, Exception exception) {
        StringBuilder sb = new StringBuilder();
        sb.append("方法调用日志 - ")
                .append("类: ").append(logEntity.getClassName()).append(", ")
                .append("方法: ").append(logEntity.getMethodName()).append(", ")
                .append("描述: ").append(logEntity.getMethodDescription()).append(", ")
                .append("状态: ").append(logEntity.getStatus() == 1 ? "成功" : "失败").append(", ")
                .append("耗时: ").append(logEntity.getCostTime()).append("ms");

        if (exception != null) {
            sb.append(", 错误: ").append(exception.getMessage());
        }

        return sb.toString();
    }

    /**
     * 获取异常堆栈信息
     */
    private String getStackTraceAsString(Exception e) {
        StringBuilder sb = new StringBuilder();
        sb.append(e.getClass().getName()).append(": ").append(e.getMessage()).append("\n");
        for (StackTraceElement element : e.getStackTrace()) {
            sb.append("    at ").append(element.toString()).append("\n");
        }
        return sb.toString();
    }

    /**
     * 获取客户端真实IP
     */
    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 处理多代理情况，取第一个IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }
}
